<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="./js/react.js"></script>
    <script src="./js/react-dom.js"></script>
    <script src="http://cdn.bootcss.com/babel-core/5.8.38/browser.min.js"></script>
</head>
<body>
    <div id="reactContainer"></div>
    

<!-- 2. 环境搭建和HelloWorld程序-->
    <!-- <script>
        var helloComponent=React.createClass({  //注册一个组件类HelloComponent,
            render:function(){
                return React.createElement('h1',null,'hello world');  //调用React.createElement实现了以下HTML内容：	<h1>< Hello world</h1>
            }
        })
        ReactDOM.render(
            React.createElement(helloComponent,null),//用于将模板转为HTML语言，并插入指定的DOM节点。
            document.getElementById('reactContainer')
        )
    </script> -->



<!-- 3. 初识JSX语法-->
    <!-- 
        优点：
        可以使用熟悉的语法仿照HTML来定义虚拟DOM。
        程序代码更加直观。
        于JavaScript之间等价转换，代码更加直观。
     -->
    <!-- <script type='text/babel'>  //为了支持JSX，这个要加上type='text/babel'
        // var HelloComponent=React.createClass({  //HelloComponent命名需要驼峰
        //     render:function(){
        //         return <h1>hello world</h1>;
        //     }
        // })
        // ReactDOM.render(
        //     <HelloComponent/>,
        //     document.getElementById('reactContainer')
        // )

        // JSX中的表达式  
        // 只要使用{}括号   需要注意的是表达式不支持if…else这样的语句，但是支持三元运算符和二元运算符。
        var HelloComponent=React.createClass({  //HelloComponent命名需要驼峰
            render:function(){
                return <h1>Hello {this.props.name?this.props.name:'world'}</h1>;
            }
        })
        ReactDOM.render(
            <HelloComponent name='Dfei'/>,
            document.getElementById('reactContainer')
        )

    </script> -->



<!-- 4. 进阶JSX语法-->
    <!-- <script type="text/babel">
        let names=['jspan','Dfei','baobao'];

        // JSX上的数组输出

        // key的作用是生成虚拟DOM时，需要使用key来进行标记,DOM更新时进行比较。  必须
        var HelloComponent=React.createClass({
            render:function(){
                return <div> //return 不能有有回车，否则报错了
                       {names.map(function(name,index){
                           return <div key={index}>Hello,{name}</div>
                       })} 
                    </div>
            }
        })
        ReactDOM.render(
            <HelloComponent/>,
            document.getElementById('reactContainer')
        )


        // 数组中的JSX

        
        // 数组中可以直接使用JSX语法
        let arr=[
            <h1 key="1">Hello world!</h1>,
            <h2 key="2"> React is awesome</h2>
        ]
        // JSX允许直接在模版插入JavaScript变量。如果这个变量是一个数组，则会展开这个数组的所有成员。
        ReactDOM.render(
            <div>
                {arr}
            </div>,
            document.getElementById('reactContainer')
        )
    </script> -->




<!-- 5. React组件：state成员-->
    <!-- 概念
        
        state成员
            组件总是需要和用户互动的。React的一大创新，就是将界面组件看成一个状态机，用户界面拥有不同状态并根据状态进行渲染输出，用户界面和数据始终保持一致。开发者的主要工作就是定义state，并根据不同的state渲染对应的用户界面。

        setState()
            通知React组件数据发生变化的方法是调用成员函数setState(data,callback)。这个函数会合并data到this.state,并重新渲染组件。渲染完成后，调用可选的callback回调。（大部分情况下不需要调用回调，因为React会负责把界面更新到最新状态）
    -->
    <!-- <script type='text/babel'>
        var TextBoxCompontent=React.createClass({
            getInitialState:function(){  //相当于vue的data，就是一个方法，同时需要里面有一个return
                return {
                    enable:false
                }
            },
            handClick:function(){
                this.setState({  //设置State的值 --》相当于vue里面的，可以改变data里面的值
                    enable:!this.state.enable
                })
            },
            render:function(){ //相当于组件的html，后期这个是在单独的一个文件了
                return (  //借用一个（），那就可以随心所欲的html变好看了
                    <p>
                        <input type='text' disabled={this.state.enable}/>
                        <button onClick={this.handClick}>改变textbox状态</button>
                    </p>
                )
            }
        })
        ReactDOM.render(
            <TextBoxCompontent/>,
            document.getElementById('reactContainer')
        )

        /** 注意点
            getInitialState函数必须有返回值，可以是null,false,一个对象。
            访问state数据的方法是”this.state.属性名”。
            变量用{}包裹，不需要再加双引号。  -》这样容易忘记掉
        */ 
    </script> -->



<!-- 6. React组件：props和render成员-->

    <!-- 概念
            props是组件固有属性的集合，其数据由外部传入，一般在整个组件的生命周期中都是只读的。属性的初识值通常由React.createElement函数或者JSX中标签的属性值进行传递，并合并到组件实例对象的this.props中。
    -->



    <!-- <script type="text/babel">
        let HelloBoxCompontent=React.createClass({
            render:function(){
                return (
                    <h2>Hello {this.props.father + '你好吗'}</h2>
                )
            }
        })
        ReactDOM.render(
            <HelloBoxCompontent father='Dfei'/>,
            document.getElementById('reactContainer')
        )

        /**
            自己总结：
                1. props相当于和vue的父组件给子组件传值方式一样，是单向的，不能修改，只读属性，
        */ 

        /** 
            胖哥
                我们不应该修改props中的值。事实上，如果要在组件外更改也是一个很麻烦的事，需要找到组件实例，这没有必要。在设计组件时，就要想好组件哪些使用state，哪些使用props集合，那些使用state集合。通常固定的组件内只读的、应由父组件传递进来的属性适合放在props集合中，如组件的类名、颜色、字体、事件响应回调函数等。
        
            props与state的区别
                props不能被其所在的组件修改，从父组件传递进来的属性不会在组件内部更改；state只能在所在组件内部更改，或在外部调用setState函数对状态进行间接修改

            render成员函数    
                首先说render是一个函数，它对于组件来说，render成员函数是必需的。render函数的主要流程是检测this.props和this.state,再返回一个单一组件实例。

            render函数应该是纯粹的，也就是说，在render函数内不应该修改组件state，不读写DOM信息，也不与浏览器交互。如果需要交互，应该在生命周期中进行交互。    
        */
    </script> -->



<!--7.React组件：生命周期  -->

    <!-- 概念
        一个组件完整的生命周期包含实例化阶段、活动阶段、销毁阶段三个阶段。每个阶段又由相应的方法管理。

        过程中涉及三个主要的动作术语：
            mounting:表示正在挂接虚拟DOM到真实DOM。
            updating:表示正在被重新渲染。
            unmounting:表示正在将虚拟DOM移除真实DOM。

        每个动作术语提供两个函数：
            componentWillMount()
            componentDidMount()
            componentWillUpdate(object nextProps, object nextState)
            componentDidUpdate(object prevProps, object prevState)
            componentWillUnmount()
    -->

    <!-- <script type="text/babel">
        let AddCount=React.createClass({
            getInitialState:function(){
                console.log('getInitialState')  //1
                return{
                    count:1
                }
            },
            componentWillMount:function(){
                console.log('componentWillMount')  //2
            },
            componentDidMount:function(){
                console.log('componentDidMount')  //3
            },
            componentWillUpdate:function(){
                console.log('componentWillUpdate') //准备更新
            },
            componentDidUpdate:function(){
                console.log('componentDidUpdate')  //更新完成
            },
            componentWillUnmount:function(){
                console.log('componentWillUnmount')
            },
            handClick:function(){
                console.log('handClick')
                this.setState({
                    count:this.state.count+1  //为什么this.state.count++ 会改变不了呢？？？转成parseInt后再++就报错，好奇怪
                })
            },
            render:function(){
                return (  //如果单独写一句，没报错，写两句，需要有一个最外层包裹，否则报错
                    <div>
                        <h1>{this.state.count}</h1>
                        <button onClick={this.handClick}>点击改变数字</button>
                    </div>
                )
            }
        })
        ReactDOM.render(
            <AddCount/>,
            document.getElementById('reactContainer')
        )
    </script> -->


<!--8.React组件小实例-必做练习  -->

    <!-- <script type="text/babel">
        let Hello = React.createClass({
            getInitialState:function(){
                return{
                    opacity:1.0
                }
            },
            componentDidMount:function(){
                var _this=this;
                setInterval(function(){
                    var opacity=this.state.opacity;
                    opacity-=0.05;
                    if(opacity<0.1){
                        opacity=1.0;
                    }
                    this.setState({
                        'opacity':opacity
                    })
                }.bind(this),100);  //bind是可以在里面使用this。可以不用再外面保存this下来在里面用
            },
            //样式要加2个双引号
            render:function(){
                return (
                    <div style={{opacity:this.state.opacity}}>  
                        <h2>{'Hello'+this.props.name}</h2>
                    </div>
                )
            }
        })
        ReactDOM.render(
            <Hello name='Dfei'/>,
            document.getElementById('reactContainer')
        )
    </script> -->




<!-- 9.React组件：this.props.children -->

    <!-- 概念
            this.props对象的属性与组件的属性一一对应，但有一个例外，就是this.props.children属性。它表示组件的所有子节点。
    -->

    <!-- 这节不是很理解，后期遇到再返回来理解 -->


    <!-- <script type="text/babel">
        var NotesList=React.createClass({
            render:function(){
                return (
                    <ol>
                        {React.Children.map(this.props.children,function(child){
                            return (
                                <li>
                                    {child}
                                </li>
                            )
                        })}
                    </ol>
                )
            }
        })
        ReactDOM.render(
            <NotesList>
                <span>Hello</span>
                <span>World</span>
                <span>Dfei</span>
            </NotesList>,
            document.getElementById('reactContainer')
        )

        /**
            渲染结果
            1.Hello
            2.World
            3.Dfei
        */ 

        /**
            上面代码的NoteList组件有两个span子节点，他们都可以通过this.props.children读取。这里需要注意，this.props.children的值有三种可能，如果当前组件没有子节点，他就是undfined；如果有一个子节点，数据类型是object；如果有多个子节点，数据类型就是array。所以处理this.proprs.children的时候要小心。
        */ 
    </script> -->




<!-- 10.React组件：props属性验证    getDefaultProps默认值设置-->

    <!-- 描述
            组件的属性是可以接收任何值的，但有时候我们希望对外界父级组件传递进来的属性数据进行限定，比如希望name属性不能缺少、onClick属性必须是函数类型等，这对确保组件被正确使用非常有意义。为此React引入了propTypes机制。React.PropTypes提供各种验证器（validator）来验证传入数据的有效性。当向props传入无效数据时，React会在JavaScript控制台抛出警告。

    -->
    
    <!-- <script type="text/babel">
        var Mtitle=React.createClass({
            PropTypes:{  //传入的值校验
                title:React.PropTypes.string.isRequired  //必填且字符串
            },
            getDefaultProps:function(){  //使用getDefaultProps方法可以用来设置组件属性的默认值。
                return {
                    title:'Hello Dfei'
                }
            },
            render:function(){
                return (
                    <h1>{this.props.title}</h1>
                )
            }
        })
        var data='true'
        ReactDOM.render(
           <Mtitle title={data}/>,
           document.getElementById('reactContainer') 
        )
    </script> -->




<!--11.React组件：获取真实DOM节点  -->

    <!-- 描述
            React中的DOM也是虚拟DOM（virtual DOM），这点跟我们以前讲的Vue非常类似。只有当它插入文档以后，才会变成真实的DOM。React也是在虚拟DOM发生变化时，进行比对后，只渲染变化的部分，它是React极高性能的主要原因之一。


            和vue一样的用法
    -->
    
    <script type="text/babel">
        var MyComponent=React.createClass({
            handClick:function(){
                    this.refs.myref.focus(); //获取原生DOM
            },
            render:function(){
                return(
                    <div>
                        <input  ref='myref'/> //ref
                        <button onClick={this.handClick}>点击获取输入框焦点</button>
                    </div>
                )
            }
        })
        ReactDOM.render(
            <MyComponent/>,
            document.getElementById('reactContainer')
        )

        // 由于this.refs.[refName]属性获取的是真实DOM，所以必须得到虚拟DOM插入文档以后，才能使用这个属性，否则会报错。
    </script>

</body>
</html>